package torres.item;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import torres.item.effect.DamageEffect;
import torres.item.effect.Effect;
import torres.item.effect.HealEffect;
import torres.item.effect.RestoreManaEffect;
import torres.item.interactive.Interaction;
import torres.item.interactive.InteractiveItem;
import torres.item.interactive.Teleporter;
import torres.item.interactive.TrapItem;
import torres.item.oneshot.OneShotItem;
import torres.map.Location;

/**
 * @author Torres
 * Reads in and instantiates all possible items from an item file and then
 * acts as a repository for those items. Other classes may request an
 * item be constructed based on the item ID.
 */
public class ItemManager {
	private static ItemManager instance;
	
	private Map<Integer, TakeableItem> takeables;
	private Map<Integer, EquippableItem> equippables;
	private Map<Integer, InteractiveItem> interactives;
	private Map<Integer, OneShotItem> oneshots;
	private Map<Integer, ObstacleItem> obstacles;
	private Map<Integer, TrapItem> traps;
	
	/**
	 * Creates a new ItemManager.
	 */
	private ItemManager() {
		takeables = new HashMap<Integer, TakeableItem>();
		equippables = new HashMap<Integer, EquippableItem>();
		interactives = new HashMap<Integer, InteractiveItem>();
		oneshots = new HashMap<Integer, OneShotItem>();
		obstacles = new HashMap<Integer, ObstacleItem>();
		traps = new HashMap<Integer, TrapItem>();
	}
	
	/**
	 * Retrieves the single instance of ItemManager.
	 * @return The single instance of ItemManager.
	 */
	public static ItemManager getInstance() {
		if(instance == null)
			instance = new ItemManager();
		return instance;
	}
	
	/**
	 * Initialize the items from the specified file to be retrieved later.
	 * @param file The file to be parsed.
	 */
	public void generate(File file) {
		try {
			Scanner reader = new Scanner(file);
			
			//read in Takeable items
			reader.nextLine(); //throw away the "--Takeable--" String
			String line;
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Effect> effects = new ArrayList<Effect>();
				for(int i = 2; i < data.length; i+=2) {
					char c = data[i].charAt(0);
					switch(c) {
						case 'H': effects.add(new HealEffect(Integer.parseInt(data[i+1]))); break;
						case 'M': effects.add(new RestoreManaEffect(Integer.parseInt(data[i+1]))); break;
						case 'D': effects.add(new DamageEffect(Integer.parseInt(data[i+1]))); break;
						default: throw new Exception(c + " is not a valid effect");
					}
				}
				TakeableItem t = new TakeableItem(name, effects);
				takeables.put(id, t);
			}
			
			//read in Equippable items
			reader.nextLine(); //throw away the "--Equippable--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				EquippableItem.Type equipType = EquippableItem.Type.valueOf(data[2]); 
				int offMod = Integer.parseInt(data[3]);
				int armMod = Integer.parseInt(data[4]);
				EquippableItem t = new EquippableItem(name, offMod, armMod, equipType);
				equippables.put(id, t);
			}
			
			//read in Interactive items
			reader.nextLine(); //throw away the "--Interactive--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Interaction> effects = new ArrayList<Interaction>();
				for(int i = 2; i < data.length;) {
					char c = data[i].charAt(0);
					switch(c) {
						case 'H': effects.add(new HealEffect(Integer.parseInt(data[i+1]))); i+=2; break;
						case 'M': effects.add(new RestoreManaEffect(Integer.parseInt(data[i+1]))); i+=2; break;
						case 'P': effects.add(new Teleporter(data[i + 1] + ".map", new Location(Integer.parseInt(data[i+2]), Integer.parseInt(data[i+3])))); i+=4; break;
						case 'D': effects.add(new DamageEffect(Integer.parseInt(data[i+1]))); i+=2; break;
						default: throw new Exception(c + " is not a valid effect");
					}
				}
				InteractiveItem t = new InteractiveItem(name, effects);
				interactives.put(id, t);
			}
			
			//read in Trap items
			reader.nextLine(); //throw away the "--Trap--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Interaction> effects = new ArrayList<Interaction>();
				for(int i = 2; i < data.length; i+=2) {
					char c = data[i].charAt(0);
					switch(c) {
						case 'H': effects.add(new HealEffect(Integer.parseInt(data[i+1]))); break;
						case 'M': effects.add(new RestoreManaEffect(Integer.parseInt(data[i+1]))); break;
						case 'D': effects.add(new DamageEffect(Integer.parseInt(data[i+1]))); break;
						default: throw new Exception(c + " is not a valid effect");
					}
				}
				TrapItem t = new TrapItem(name, effects);
				traps.put(id, t);
			}
			
			//read in OneShot items
			reader.nextLine(); //throw away the "--OneShot--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Effect> effects = new ArrayList<Effect>();
				for(int i = 2; i < data.length; i+=2) {
					char c = data[i].charAt(0);
					switch(c) {
						case 'H': effects.add(new HealEffect(Integer.parseInt(data[i+1]))); break;
						case 'M': effects.add(new RestoreManaEffect(Integer.parseInt(data[i+1]))); break;
						case 'D': effects.add(new DamageEffect(Integer.parseInt(data[i+1]))); break;
						default: throw new Exception(c + " is not a valid effect");
					}
				}
				OneShotItem t = new OneShotItem(name, effects);
				oneshots.put(id, t);
			}
			
			//read in Obstacle items
			reader.nextLine(); //throw away the "--Obstacle--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split(" ");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				ObstacleItem t = new ObstacleItem(name);
				obstacles.put(id, t);
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Creates and returns a new instance of an TakeableItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The TakeableItem corresponding to the specified key.
	 */
	public TakeableItem getTakeableItem(int key) {
		return new TakeableItem(takeables.get(key));
	}
	
	/**
	 * Creates and returns a new instance of an EquippableItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The EquippableItem corresponding to the specified key.
	 */
	public EquippableItem getEquippableItem(int key) {
		return new EquippableItem(equippables.get(key));
	}
	
	/**
	 * Creates and returns a new instance of an InteractiveItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The InteractiveItem corresponding to the specified key.
	 */
	public InteractiveItem getInteractiveItem(int key) {
		return new InteractiveItem(interactives.get(key));
	}
	
	/**
	 * Creates and returns a new instance of an OneShotItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The OneShotItem corresponding to the specified key.
	 */
	public OneShotItem getOneShotItem(int key) {
		return new OneShotItem(oneshots.get(key));
	}
	
	/**
	 * Creates and returns a new instance of an ObstacleItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The ObstacleItem corresponding to the specified key.
	 */
	public ObstacleItem getObstacleItem(int key) {
		return new ObstacleItem(obstacles.get(key));
	}
	
	/**
	 * Creates and returns a new instance of a TrapItem that corresponds to the specified key.
	 * @param key The key of the item to be retrieved.
	 * @return The TrapItem corresponding to the specified key.
	 */
	public TrapItem getTrapItem(int key) {
		return new TrapItem(traps.get(key));
	}
}
